import { ConstantEnum } from '@libs/common/enums';
import {
  BaseRepository,
  indexPaginateBuilder,
  showBuilderEntity,
} from '@libs/common/repository';
import { Inject, Injectable } from '@nestjs/common';
import { Dayjs, FormatDate, FormatDateTime } from '@shencom/utils-date';
import { plainToInstance } from 'class-transformer';
import { chunk } from 'lodash';
import { Between, DataSource, Equal, In, IsNull, MoreThan, Not } from 'typeorm';

import { AttendanceStatisticsService } from '../attendance-statistics/attendance-statistics.service';
import { OvertimeApplyService } from '../overtime-apply/overtime-apply.service';
import {
  CountOvertimeLogDto,
  CreateOvertimeLogDto,
  DeleteOvertimeLogDto,
  IndexOvertimeLogDto,
  ShowOvertimeLogDto,
  SyncOvertimeLogDto,
  UpdateOvertimeLogDto,
} from './dto/overtime-log.dto';
import { OvertimeLog } from './entities/overtime-log.entity';

interface UserCountVo {
  dingId: string;
  userName?: string;
  /** 单位小时 */
  totalTime: number;
}

@Injectable()
export class OvertimeLogService extends BaseRepository {
  constructor(
    @Inject(ConstantEnum.DB_DATA_SOURCE_MANAGER) dataSource: DataSource,
  ) {
    super(dataSource);
  }

  protected repository = this.getRepository(OvertimeLog);

  protected repositoryManager = () => this.getRepository(OvertimeLog);

  @Inject(AttendanceStatisticsService)
  private attendanceStatisticsService: AttendanceStatisticsService;

  @Inject(OvertimeApplyService)
  private overtimeApplyService: OvertimeApplyService;

  async index(dto: IndexOvertimeLogDto) {
    const res = await indexPaginateBuilder({
      dto,
      repository: this.repository,
    });
    return res;
  }

  show<D extends ShowOvertimeLogDto = ShowOvertimeLogDto>(dto: D) {
    return showBuilderEntity(this.repository, dto);
  }

  async delete(dto: DeleteOvertimeLogDto) {
    const { id, ids } = dto;
    if (id) {
      await this.repositoryManager().delete({ id: Equal(id) });
    } else if (ids) {
      await this.repositoryManager().delete({ id: In(ids) });
    }
    return;
  }

  update(dto: UpdateOvertimeLogDto | UpdateOvertimeLogDto[]) {
    const dtos = Array.isArray(dto) ? dto : [dto];
    const entities = plainToInstance(OvertimeLog, dtos);

    return this.repositoryManager().save(entities);
  }

  create(dto: CreateOvertimeLogDto | CreateOvertimeLogDto[]) {
    const entity = this.repositoryManager().create(
      plainToInstance(OvertimeLog, dto),
    );

    return this.repositoryManager().insert(entity);
  }

  import() {
    // CODE
  }

  export() {
    // CODE
  }

  switch(id: string, status: boolean) {
    // status 需要在 entity 中定义, 如果没有不需要这个接口
    // return this.repository.update({ id }, { status });
  }

  async count(dto: CountOvertimeLogDto) {
    const result = await this.repository
      .createQueryBuilder('o')
      .select('o.dingId', 'dingId')
      .addSelect('o.userName', 'userName')
      .addSelect('CAST(SUM(o.time) AS UNSIGNED)', 'totalTime')
      .where('o.dingId = :dingId', { dingId: dto.dingId })
      .groupBy('o.dingId')
      .addGroupBy('o.userName')
      .getRawOne<UserCountVo>();

    return result;
  }

  async countTime(dto: CountOvertimeLogDto) {
    return Number((await this.count(dto))?.totalTime) || 0;
  }

  async sync(dto: SyncOvertimeLogDto) {
    const { date } = dto; // 2024-06
    const startMonth = FormatDateTime(Dayjs(date).startOf('month').valueOf());
    const endMonth = FormatDateTime(Dayjs(date).endOf('month').valueOf());

    // 使用组合键进行查询
    const records = await this.repository.find({
      where: { workDate: Between(startMonth, endMonth) },
    });

    const res = await this.attendanceStatisticsService.find({
      workDate: Between(startMonth, endMonth),
      shouldAttendanceDays: Not(IsNull()),
      offDutyUserCheckTime: MoreThan('19:00:00'),
      id: Not(In(records.map((v) => v.attendanceStatisticsId))),
    });

    if (!res.length) return;

    const plain = res
      .map((v) => {
        const workDate = FormatDate(Dayjs(v.workDate).valueOf());
        const 开始时间 = Dayjs(`${workDate} 19:00`);

        const 结束时间 = Dayjs(`${workDate} ${v.offDutyUserCheckTime}`);
        const time = 结束时间.diff(开始时间, 'minute');

        if (!time) return null;

        return {
          dingId: v.dingId,
          userName: v.userName,
          workDate: v.workDate,
          time,
          attendanceStatisticsId: v.id,
        };
      })
      .filter(Boolean);

    const instance = plainToInstance(OvertimeLog, plain);

    const list = chunk(instance, 500);

    for (const item of list) {
      await this.repositoryManager().insert(item);
    }

    return instance;
  }
}
